package com.asen.commons.core.jdbc.handler;

import com.asen.commons.exception.ResultMapException;
import com.asen.commons.exception.TypeException;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;

/**
 * Sql类型处理器
 *
 * @author Gary
 * @version 1.0
 * @date 2018-12-02
 */
public abstract class BaseTypeHandler<T> implements TypeHandler<T> {

    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter) throws Exception {
        if (parameter == null) {
            if (getSqlType() == -1) {
                throw new TypeException("服务内部错误，请联系管理员！", "第 #" + i + "个参数的SqlType类型不确定！");
            }
            try {
                ps.setNull(i, getSqlType());
            } catch (SQLException e) {
                throw new TypeException("服务内部错误，请联系管理员！", "设置第 #" + i + " 个参数为【null】失败！", e);
            }
        } else {
            try {
                setNonNullParameter(ps, i, getValue(parameter));
            } catch (Exception e) {
                throw new TypeException("服务内部错误，请联系管理员！", "设置第 #" + i + " 个参数为【" + parameter + "】失败！", e);
            }
        }
    }

    @Override
    public T getResult(ResultSet rs, String columnName) throws Exception {
        T result;
        try {
            result = getNullableResult(rs, columnName);
        } catch (Exception e) {
            throw new ResultMapException("服务内部错误，请联系管理员！", "转换 '" + columnName + "' 结果失败: ", e);
        }
        if (rs.wasNull()) {
            return null;
        } else {
            return result;
        }
    }

    @Override
    public T getResult(ResultSet rs, int columnIndex) throws Exception {
        T result;
        try {
            result = getNullableResult(rs, columnIndex);
        } catch (Exception e) {
            throw new ResultMapException("服务内部错误，请联系管理员！", "转换 #" + columnIndex + " 结果失败: " + e, e);
        }
        if (rs.wasNull()) {
            return null;
        } else {
            return result;
        }
    }

    @Override
    public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
        T result;
        try {
            result = getNullableResult(cs, columnIndex);
        } catch (Exception e) {
            throw new ResultMapException("服务内部错误，请联系管理员！", "转换 #" + columnIndex + " 结果失败: " + e, e);
        }
        if (cs.wasNull()) {
            return null;
        } else {
            return result;
        }
    }

    protected Class<T> getRefRawType() {
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];

        if (rawType instanceof ParameterizedType) {
            rawType = ((ParameterizedType) rawType).getRawType();
        }

        return (Class<T>) rawType;
    }

    /**
     * 设置非null值参数
     *
     * @param ps
     * @param i
     * @param parameter
     * @throws SQLException
     */
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter) throws Exception;

    /**
     * 返回结果
     *
     * @param rs
     * @param columnName
     * @return
     * @throws SQLException
     */
    public abstract T getNullableResult(ResultSet rs, String columnName) throws Exception;

    /**
     * 返回结果
     *
     * @param rs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws Exception;

    /**
     * 返回结果
     *
     * @param cs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws Exception;

    @Override
    public String toSqlValue(Object value) throws Exception {
        if (value == null) {
            return null;
        }

        return value.toString();
    }

    protected String getSqlValue(Collection collection, boolean isVarcharType) {
        StringBuilder sb = new StringBuilder(0);
        for (Object s : collection) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            if (isVarcharType) {
                sb.append("\'" + s + "\'");
            } else {
                sb.append(s);
            }
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

    protected String getSqlValue(String[] sArray, boolean isVarcharType) {
        StringBuilder sb = new StringBuilder(0);
        for (String s : sArray) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            if (isVarcharType) {
                sb.append("\'" + s + "\'");
            } else {
                sb.append(s);
            }
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

    protected String getSqlValue(Long[] sArray) {
        StringBuilder sb = new StringBuilder(0);
        for (Long s : sArray) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(s);
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

    protected String getSqlValue(long[] sArray) {
        StringBuilder sb = new StringBuilder(0);
        for (Long s : sArray) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(s);
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

    protected String getSqlValue(Integer[] sArray) {
        StringBuilder sb = new StringBuilder(0);
        for (Integer s : sArray) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(s);
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

    protected String getSqlValue(int[] sArray) {
        StringBuilder sb = new StringBuilder(0);
        for (Integer s : sArray) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(s);
        }
        sb.insert(0, "(");
        sb.append(")");
        return sb.toString();
    }

}
